package com.stropheware.droidDefense;

import java.util.ArrayList;

import com.stropheware.pathfinding.Node;
import com.stropheware.pathfinding.Path;
import com.stropheware.pathfinding.PathFinder;
import com.stropheware.pathfinding.UnionFind;
import com.stropheware.pathfinding.Path.Step;
import com.stropheware.util.MethodTimer;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;

public class GameThread extends Thread{
	
	private static final String TAG = "GameThread";
	
	private SurfaceHolder surfaceHolder;
	
	private GameBoard gameBoard;
		
	private Menu menu;
	private PlayerMenu playerMenu;
	
	private Context context;
	
	private Drawable tempTowerDrawable;
	private boolean drawingTempBasicTowerDrawable;
	
	private int towerRangeX;
	private int towerRangeY;
	
	public PathFinder pathFinder;
	public Path currentPath;
	private Paint rangePaint;
	private Paint toPlacePaint;
	
	int boundsX;
	int boundsY;
	
	int toPlacetop;
	int toPlaceleft;
	
	Rect toPlace;
	
	/** Used for pop ups for enemy/tower*/
	private AbstractTower activeTower;
	private AbstractEnemy activeEnemy;
	
	private GameManager gameManager;
	
	private UnionFind unionFind;

	private static final MethodTimer drawTimer = new MethodTimer();
	
	//Primitive, testing purposes
	//public static ArrayList<Cell> towerCellList;
	boolean anim = true;
	public static final int BASIC_TOWER = 1;
	public static final int AOE_TOWER = 2;
	public static final int POWER_TOWER = 3;
	public static final int SUPPORT_TOWER = 4;
	public static final int UTILITY_TOWER = 5;
	
	public static int towerType;
	
	
	/** Indicate whether the surface has been created & is ready to draw */
    private boolean runThread = false;
    
    /**One instance drawables*/
    private final Rect pathRect;
    private final Drawable basicTower;
    
	public GameThread(SurfaceHolder surfaceHolder, int width, int height, Context context) {
		this.context = context;
		this.surfaceHolder = surfaceHolder;
		gameBoard = new GameBoard(width, height, context);
		unionFind = new UnionFind(gameBoard);
		menu = new Menu(gameBoard, context);
		gameManager = new GameManager(gameBoard, this, context);
		playerMenu = new PlayerMenu(gameBoard, gameManager, context);
		initTempDrawables();
		resetDrawingBooleans();
		pathRect = new Rect();
		basicTower = context.getResources().getDrawable(R.drawable.basic_tower);
		activeTower = new BasicTower();
		activeTower.display=false;
		activeEnemy = new BalancedEnemy();
		pathFinder = new PathFinder(gameBoard,500, false);
		updateCurrentPath();
		
	}
	
	private void resetDrawingBooleans() {
		drawingTempBasicTowerDrawable = false;
	}
	
	public void acceleratorShaken() {
		Log.d(TAG, "accel shaken");
		gameManager.startRound();
	}

	public boolean doOnKeyDown(int keyCode, KeyEvent event){
		Log.d(TAG, "Key pressed.");
			gameManager.startRound();
			return true;

		
	}
	
	private void initTempDrawables() {
		toPlace = new Rect();
		rangePaint = new Paint();
		rangePaint.setColor(0xaa00cc00);
		rangePaint.setStrokeWidth(10);
		rangePaint.setAntiAlias(true);
		toPlacePaint = new Paint();
		toPlacePaint.setColor(0xaa00cc00);
		toPlacePaint.setAntiAlias(true);
		activeTower = new BasicTower(context, gameBoard, 0, 0);
	}
	
	public void doDraw(Canvas canvas) {
		drawTimer.start();
		canvas.drawColor(Color.WHITE);
		drawPath(canvas);
		gameBoard.drawGameBoard(canvas);
		menu.drawMenu(canvas);
		playerMenu.drawMenu(canvas);
		gameManager.drawGame(canvas);
		drawTemporaryDrawables(canvas);
		drawTowerPopUp(canvas);
		drawEnemyPopUp(canvas);
		drawTimer.end();
		//Log.d(TAG,"Draw time: " + drawTimer.getTimeInMilliseconds());
		
	}
	
	private void drawTemporaryDrawables(Canvas canvas) {
		if(drawingTempBasicTowerDrawable){
			canvas.drawCircle(towerRangeX, towerRangeY, gameBoard.towerWidthPx, rangePaint);
			toPlacetop = tempTowerDrawable.getBounds().top;
			toPlaceleft = tempTowerDrawable.getBounds().left;
			toPlace.set(toPlaceleft, toPlacetop, toPlaceleft+gameBoard.towerWidthPx, toPlacetop+gameBoard.towerWidthPx);
			canvas.drawRect(toPlace, toPlacePaint);
			tempTowerDrawable.draw(canvas);
		}
	}
	
	
	/*
	 * Called whenever screen is touched
	 * Must be efficient
	 */
	public void onTouch(MotionEvent event) {
		
		if(event.getAction() == MotionEvent.ACTION_DOWN) {
			
			if(activeTower.display){
				if(isTouchEventOnUpgrade(event)){
					activeTower.upgrade();
					//Log.d(TAG,"UPGRADE");
				}
				else if(isTouchEventOnSell(event)){
					unionFind.removeTower(activeTower);
					activeTower.sell(gameManager);
					//Log.d(TAG,"SELL");
				}
				activeTower.display = false;
			}
			
			else if	(activeEnemy.displayPopUp)activeEnemy.displayPopUp = false;
			//Log.d(TAG, "Screen touched down: (x,y) " + event.getX() + "," + event.getY());
			else if(isTouchEventInMenu(event)) {
				
				//Log.d(TAG, "Down-Clicked in menu");
				if(menu.touchFirstMenuTower(event)) {
					//Log.d(TAG, "Down-Clicked in first MenuTower");
					tempTowerDrawable = context.getResources().getDrawable(R.drawable.basic_tower);
					setTempDrawableBounds(tempTowerDrawable, event);
					towerType = BASIC_TOWER;
					drawingTempBasicTowerDrawable = true;
				}else if (menu.touchSecondMenuTower(event)) {
					//Log.d(TAG, "Down-Clicked in second MenuTower");
					tempTowerDrawable = context.getResources().getDrawable(R.drawable.aoe_tower);
					setTempDrawableBounds(tempTowerDrawable, event);
					towerType = AOE_TOWER;
					drawingTempBasicTowerDrawable = true;
				}else if (menu.touchThirdMenuTower(event)) {
					//Log.d(TAG, "Down-Clicked in third MenuTower");
					tempTowerDrawable = context.getResources().getDrawable(R.drawable.power_tower);
					setTempDrawableBounds(tempTowerDrawable, event);
					towerType = POWER_TOWER;
					drawingTempBasicTowerDrawable = true;
				}else if (menu.touchFourthMenuTower(event)) {
					//Log.d(TAG, "Down-Clicked in fourth MenuTower");
					tempTowerDrawable = context.getResources().getDrawable(R.drawable.support_tower);
					setTempDrawableBounds(tempTowerDrawable, event);
					towerType = SUPPORT_TOWER;
					drawingTempBasicTowerDrawable = true;
				}else if (menu.touchFifthMenuTower(event)) {
					//Log.d(TAG, "Down-Clicked in fifth MenuTower");
					tempTowerDrawable = context.getResources().getDrawable(R.drawable.utility_tower);
					setTempDrawableBounds(tempTowerDrawable, event);
					towerType = UTILITY_TOWER;
					drawingTempBasicTowerDrawable = true;
				}
			}else if (isTouchEventInNothing(event)){
				//Log.d(TAG, "Down-Clicked on nothing");
			}else {
				//Log.d(TAG, "Down-Clicked on gameboard");
				Cell tempCell = gameBoard.getNearestCell((int)event.getX(), (int)event.getY());
				AbstractTower tempTower = tempCell.occupiedTower;
				if(isTower(tempTower)){ activeTower = tempTower; activeTower.showPopUp();}
				else if(clickedOnEnemy(event.getX(),event.getY())) {activeEnemy.displayPopUp = true;}
			}
		}
		else if(event.getAction() == MotionEvent.ACTION_MOVE) {
			//Log.d(TAG, "In movement: (x,y) " + event.getX() + "," + event.getY());
			if(drawingTempBasicTowerDrawable) {
				setTempDrawableBounds(tempTowerDrawable, event);
			}
		}
		else if(event.getAction() == MotionEvent.ACTION_UP) {
			//Log.d(TAG, "Screen touch released: (x,y) " + event.getX() + "," + event.getY());
			if(isTouchEventInMenu(event) && !drawingTempBasicTowerDrawable) {
				//Log.d(TAG, "Release-Clicked in menu");
			}else if (isTouchEventInNothing(event) && !drawingTempBasicTowerDrawable){
				//Log.d(TAG, "Release-Clicked on nothing");
			}else { //drops here if drawingTempBasicTowerDrawable
				//Log.d(TAG, "Release-Clicked on gameboard");
				if(drawingTempBasicTowerDrawable) {
					//Log.d(TAG, "Placed basicTower on gameboard");
					Rect boundRect = tempTowerDrawable.getBounds();
					Cell tempCell = gameBoard.getCellNumIndex(boundRect.top, boundRect.left);
					if(isValidPlacement(tempTowerDrawable.getBounds().left,tempTowerDrawable.getBounds().top)) {
						if(!gameBoard.isOnCurrentPath(tempCell)){
							AbstractTower newTower = getTowerType(context, gameBoard, boundRect.top, boundRect.left, towerType);//new BasicTower(context, gameBoard, boundRect.top, boundRect.left);
							gameManager.towerManager.addTower(tempCell, newTower);
							unionFind.addTower(newTower);
						}
						else {
							gameBoard.addTempTower(tempCell);
							MethodTimer mt = new MethodTimer();
							mt.start();
							updateCurrentPath();
							mt.end();
							Log.d(TAG, "pathfinding took: " + mt.getTimeInMilliseconds() + " milliseconds");
							if(currentPath != null) {
								gameBoard.removeTempTower(tempCell);
								AbstractTower newTower = getTowerType(context, gameBoard, boundRect.top, boundRect.left, towerType);//new BasicTower(context, gameBoard, boundRect.top, boundRect.left);
								gameManager.towerManager.addTower(tempCell, newTower);
								unionFind.addTower(newTower);
								//gameBoard.addTowerGameBoard(tempCell, new BasicTower(context, gameBoard, boundRect.top, boundRect.left));
							}else {
								gameBoard.removeTempTower(tempCell);
								updateCurrentPath();
							}
						}
					}

				}
			}
			resetDrawingBooleans();
		}
	}
	
	private boolean isTower(AbstractTower tower){
		if(tower == null)return false;
		else return true;
	}
	
	private void setTempDrawableBounds(Drawable drawable, MotionEvent event) {
		boundsX = (int)(event.getX())+gameBoard.startXPixel-((int)event.getX()%gameBoard.cellSideLength);
		boundsY = (int)event.getY()+gameBoard.startYPixel-((int)event.getY()%gameBoard.cellSideLength);
		drawable.setBounds(boundsX - 2 * gameBoard.towerWidthPx, boundsY - (gameBoard.towerWidthPx/2), boundsX -  gameBoard.towerWidthPx , boundsY + (gameBoard.towerWidthPx/2));
		towerRangeX = (int)(boundsX - 1.5 * gameBoard.towerWidthPx);
		towerRangeY = (int)boundsY;
		
		if(!isValidPlacement(drawable.getBounds().left,drawable.getBounds().top))
			toPlacePaint.setColor(0xaacc0000);
		else
			toPlacePaint.setColor(0xaa00cc00);	
	}
	
	private boolean isTouchEventInMenu(MotionEvent event) {
		return (event.getX() >= (gameBoard.actualWidth - gameBoard.actualMenuWidth));
	}
	
	private boolean isValidPlacement(int x, int y){
		/* Check if actually in gameBoard */
		Cell topLeft, bottomRight, topRight, bottomLeft;
		try{
			//Log.d(TAG,"Xinc: " + x + " Xconv: "+ (x-gameBoard.startXPixel)/gameBoard.cellSideLength + " Yinc " + y + " Yconv: " + (y+gameBoard.startYPixel)/gameBoard.cellSideLength);	
			topLeft = gameBoard.cellGrid[(x-gameBoard.startXPixel)/gameBoard.cellSideLength][(y-gameBoard.startYPixel)/gameBoard.cellSideLength];
			bottomRight = gameBoard.cellGrid[(x+gameBoard.startXPixel+gameBoard.towerWidthPx-gameBoard.cellSideLength)/gameBoard.cellSideLength][(y+gameBoard.startYPixel+gameBoard.towerWidthPx-gameBoard.cellSideLength)/gameBoard.cellSideLength];
			topRight = gameBoard.cellGrid[(x+gameBoard.startXPixel+gameBoard.towerWidthPx-gameBoard.cellSideLength)/gameBoard.cellSideLength][(y-gameBoard.startYPixel)/gameBoard.cellSideLength];
			bottomLeft = gameBoard.cellGrid[(x-gameBoard.startXPixel)/gameBoard.cellSideLength][(y+gameBoard.startYPixel+gameBoard.towerWidthPx-gameBoard.cellSideLength)/gameBoard.cellSideLength];
		}
		catch(ArrayIndexOutOfBoundsException e){return false;}
		if(!gameManager.towerManager.isValidPlacement(topLeft, bottomRight, topRight, bottomLeft)) return false;
		if(topLeft.content == Cell.RESERVED || bottomRight.content == Cell.RESERVED ||
				topRight.content == Cell.RESERVED || bottomLeft.content == Cell.RESERVED) return false;
		if(!unionFind.isValidPlacement(topLeft)) return false;
		return true;
	}
	
	/*
	 * InNothing meaning neither inMenu or in GameBoard
	 * Relies on isTouchEventInMenu being run first
	 */
	private boolean isTouchEventInNothing(MotionEvent event) {
		if(event.getY() >= gameBoard.actualHeight - gameBoard.actualBottomMenuHeight) return true;
		if(event.getX() >= (gameBoard.actualWidth - gameBoard.actualMenuWidth - 
				gameBoard.startYPixel)) return true;
		if(event.getX() < gameBoard.startYPixel) return true;
		if(event.getY() < gameBoard.startXPixel) return true;
		return false;
	}
	
	public boolean isTouchEventOnUpgrade(MotionEvent event){
		if(event.getX() >= AbstractTower.upgradeBounds[0] &&
			event.getX() <= AbstractTower.upgradeBounds[2] &&
			event.getY() >= AbstractTower.upgradeBounds[1] &&
			event.getY() <= AbstractTower.upgradeBounds[3]) 
				return true;
		else
				return false;
	}
	
	public boolean isTouchEventOnSell(MotionEvent event){
		if(event.getX() >= AbstractTower.sellBounds[0] &&
				event.getX() <= AbstractTower.sellBounds[2] &&
				event.getY() >= AbstractTower.sellBounds[1] &&
				event.getY() <= AbstractTower.sellBounds[3]) 
					return true;
			else
					return false;
	}
	
	private boolean clickedOnEnemy(float x, float y){
		int activeEnemyIndex = this.gameManager.enemyManager.activeEnemy;
		int breakCondition = this.gameManager.enemyManager.enemyArray[activeEnemyIndex].length;
		for(int i = 0; i < breakCondition; i++){
			if( x >= this.gameManager.enemyManager.enemyArray[activeEnemyIndex][i].xpx &&
				x <= this.gameManager.enemyManager.enemyArray[activeEnemyIndex][i].xpx + gameBoard.cellSideLength * AbstractEnemy.SIZE &&	
				y >= this.gameManager.enemyManager.enemyArray[activeEnemyIndex][i].ypx &&
				y <= this.gameManager.enemyManager.enemyArray[activeEnemyIndex][i].ypx + gameBoard.cellSideLength * AbstractEnemy.SIZE){
					activeEnemy = this.gameManager.enemyManager.enemyArray[activeEnemyIndex][i];
					if(activeEnemy.visible)
					return true;
			}
		}
		return false;
	}
	
	private void drawTowerPopUp(Canvas canvas){
		if(activeTower.display)activeTower.drawPopUp(canvas);
		else return;
	}
	
	private void drawEnemyPopUp(Canvas canvas){
		if(activeEnemy.displayPopUp){activeEnemy.showPopUp();activeEnemy.drawPopUp(canvas);}
		else return;
	}
	
	private void drawPath(Canvas canvas){
		int left, top;
		if(currentPath != null){
			for(int i = 0; i < currentPath.steps.size(); i++){
				try {
					left = PathFinder.nodes[currentPath.steps.get(i).x][currentPath.steps.get(i).y].occupiedCells[0].pxColumn;
					top = PathFinder.nodes[currentPath.steps.get(i).x][currentPath.steps.get(i).y].occupiedCells[0].pxRow;
					pathRect.set(left, top, left+gameBoard.cellSideLength+gameBoard.cellSideLength+gameBoard.cellSideLength, top+gameBoard.cellSideLength+gameBoard.cellSideLength+gameBoard.cellSideLength);
					canvas.drawRect(pathRect, rangePaint);
				}catch(ArrayIndexOutOfBoundsException e){Log.d(TAG,"Out of bounds array!");}
			}
		}
	}
	
	public void setRunning(Boolean runThread) {
		this.runThread = runThread;
	}
	
	public void run() {
		gameManager.startGame();
		while (runThread) {
			gameManager.updateGame();
	        Canvas canvas = null;
	        try {
	            canvas = surfaceHolder.lockCanvas(null);
	            synchronized (surfaceHolder) {
	                doDraw(canvas);
	            }
	        } finally {
	            if (canvas != null) {
	                surfaceHolder.unlockCanvasAndPost(canvas);
	            }
	            try {
					Thread.sleep(25);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
	        }
		}
		Log.d(TAG, "thread exited");
    }
	
	private AbstractTower getTowerType(Context context, GameBoard gameBoard, int top, int left, int type){
		switch(type){
		case BASIC_TOWER: return new BasicTower(context, gameBoard, top, left);
		case AOE_TOWER: return new AoETower(context, gameBoard, top, left);	
		case POWER_TOWER: return new PowerTower(context, gameBoard, top, left);	
		case SUPPORT_TOWER: return new SupportTower(context, gameBoard, top, left);
		case UTILITY_TOWER: return new UtilityTower(context, gameBoard, top, left);
		default: return null;
		}
	}
	
	public void updateCurrentPath() {
		currentPath = pathFinder.findPath(gameBoard.startNode, gameBoard.targetNode, gameBoard);
		if(currentPath != null) {
			gameManager.enemyManager.updatePath(currentPath.steps);
			gameBoard.restCellPathBooleans();
			Cell [] temp;
			for(int i = 0; i < currentPath.steps.size(); i++) {
				temp = PathFinder.nodes[currentPath.steps.get(i).x][currentPath.steps.get(i).y].occupiedCells;
				for(int x = 0; x < temp.length; x++) temp[x].isInPath = true;
			}
		}
	}
}
